Skip to content

ADFA-3718 | Require scrolling to end before project creation#1321

Merged
jatezzz merged 3 commits into
stagefrom
fix/ADFA-3718-require-scroll-project-creation
May 21, 2026
Merged

ADFA-3718 | Require scrolling to end before project creation#1321
jatezzz merged 3 commits into
stagefrom
fix/ADFA-3718-require-scroll-project-creation

Conversation

@jatezzz
Copy link
Copy Markdown
Collaborator

@jatezzz jatezzz commented May 19, 2026

Description

Implemented a scroll-tracking mechanism to ensure users view all form fields and checkboxes before they can create a project. The "Finish" button is now disabled by default and only enables once the user has scrolled to the bottom of the template details. Additionally, a blinking scroll indicator was added to guide users, and the project creation logic was refactored into a dedicated manager for better maintainability.

Details

  • Added TemplateScrollGateKeeper to monitor the RecyclerView scroll state and detect when the bottom is reached.
  • Updated TemplateDetailsFragment to bind the gatekeeper to the widgets list and update the "Finish" button's enabled state dynamically.
  • Added a blinking downward arrow (scrollIndicator) next to the "Finish" button that disappears once the user has scrolled to the bottom.
  • Extracted project creation execution logic into a new ProjectCreationManager class.
document_4929672108394415729.mp4

Ticket

ADFA-3718

Observation

The TemplateScrollGateKeeper automatically resets its internal state if the layout width changes, which helps maintain accurate scroll detection across device orientation changes or resizing events.

@jatezzz jatezzz marked this pull request as ready for review May 19, 2026 22:12
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 19, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e546dfce-bb79-4b90-9982-372b689e0142

📥 Commits

Reviewing files that changed from the base of the PR and between 3713bc3 and 2fbf11f.

📒 Files selected for processing (5)
  • app/src/main/java/com/itsaky/androidide/fragments/TemplateDetailsFragment.kt
  • app/src/main/java/com/itsaky/androidide/utils/ProjectCreationManager.kt
  • app/src/main/java/com/itsaky/androidide/utils/ui/TemplateScrollGateKeeper.kt
  • app/src/main/res/layout/fragment_template_details.xml
  • resources/src/main/res/values/strings.xml

📝 Walkthrough

Release Notes - ADFA-3718: Require scrolling to end before project creation

  • Users must scroll to the bottom of the template details list before the "Finish" button becomes enabled.
  • A blinking downward arrow indicator appears next to the "Finish" button to prompt scrolling and disappears once the bottom is reached.
  • Project creation logic extracted to ProjectCreationManager to centralize execution, success/error handling, and RecentProject construction.
  • TemplateScrollGateKeeper added to monitor the RecyclerView and expose hasReachedEnd; it attaches/detaches listeners, resets on layout width changes, and provides checkIfReachedEnd()/reset() APIs.
  • TemplateDetailsFragment refactored: initialization split into helpers, binds the gatekeeper to the widgets list, updates Finish enabled state based on gatekeeper + creation state, manages the blink animator, and delegates creation to ProjectCreationManager; cleanup happens in onDestroyView.
  • UI/layout: added ImageView scrollIndicator (ic_arrow_down) adjacent to the Finish button and added string resource msg_scroll_to_create_project.

Risks & Best-practice notes

  • ⚠️ LayoutManager assumption: TemplateScrollGateKeeper uses LinearLayoutManager APIs (findLastCompletelyVisibleItemPosition()) without enforcing/type-checking the LayoutManager type — add a type check or require LinearLayoutManager to avoid silent failures with other layout managers.
  • ⚠️ Scroll-detection edge cases: Using findLastCompletelyVisibleItemPosition() and canScrollVertically(1) may fail for variable-height items, nested scrolling parents, RTL layouts, or fast programmatic scrolling. Add extra checks and UI tests to ensure reliable detection.
  • ⚠️ Listener lifecycle / leak risk: The gatekeeper registers multiple listeners; ensure detach() is always called on view teardown (currently done in onDestroyView) and consider defensive nulling and weak references to eliminate retention risk.
  • ⚠️ Accessibility: scrollIndicator is marked not important for accessibility and uses a blinking animation. This can exclude TalkBack users and may create motion-sensitivity issues. Implement TalkBack cues (announce when scrolling is required and when enabled), provide contentDescription, and respect reduced-motion accessibility settings.
  • ⚠️ Error reporting robustness: ProjectCreationManager can forward sparse or non-localized error strings. Normalize/localize error messages and improve diagnostic logging to aid support and debugging.
  • ✓ Positive: Cancelling the blink animator and detaching the gatekeeper in onDestroyView reduces leak risk. Extracting ProjectCreationManager improves maintainability and testability.

Walkthrough

Extracts scroll-end gating into TemplateScrollGateKeeper, moves template validation/execution into ProjectCreationManager, adds a scrollIndicator ImageView, and updates TemplateDetailsFragment to use these utilities for finish gating and project-creation callbacks.

Changes

Template Details Fragment: Scroll Gate and Project Creation Refactor

Layer / File(s) Summary
Scroll gate keeper and UI indicator
app/src/main/java/com/itsaky/androidide/utils/ui/TemplateScrollGateKeeper.kt, app/src/main/res/layout/fragment_template_details.xml, resources/src/main/res/values/strings.xml
Adds TemplateScrollGateKeeper to track whether the RecyclerView has reached the bottom (attach/detach/reset/check), an ImageView scrollIndicator for visual feedback, and the msg_scroll_to_create_project string resource.
Project creation orchestration
app/src/main/java/com/itsaky/androidide/utils/ProjectCreationManager.kt
Adds ProjectCreationManager.execute(...) to validate StringParameters, run the template recipe asynchronously via executeAsyncProvideError, map errors to messages, construct a RecentProject on success, and invoke onStart/onSuccess/onError callbacks.
Fragment integration and UI wiring
app/src/main/java/com/itsaky/androidide/fragments/TemplateDetailsFragment.kt
Reorganizes imports and lifecycle, initializes and detaches TemplateScrollGateKeeper, starts/cancels the scrollIndicator blinking animator, consolidates finish-button gating into updateFinishEnabledState(), posts an initial scroll check after binding widgets, extends tooltip setup, and delegates project creation to ProjectCreationManager with error/success handling and navigation.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • itsaky-adfa
  • jomen-adfa
  • hal-eisen-adfa

Poem

🐰 I blink an arrow, patient and small,
Waiting till widgets show the bottom's call.
Gatekeeper counts each scroll and then grins,
Manager spins projects from template skins.
Hop—new projects spring up, quiet wins!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: requiring scrolling to the end before enabling project creation.
Description check ✅ Passed The description is well-organized, directly related to the changeset, and provides sufficient context about the scroll-tracking mechanism and refactored project creation logic.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/ADFA-3718-require-scroll-project-creation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@app/src/main/java/com/itsaky/androidide/fragments/TemplateDetailsFragment.kt`:
- Around line 63-64: The blinking ValueAnimator created in
startBlinkingIndicator() is never stored or stopped, causing a lifecycle leak;
add a nullable property (e.g., indicatorAnimator) to TemplateDetailsFragment to
hold the animator returned by startBlinkingIndicator(), then in onDestroyView()
call indicatorAnimator?.cancel(), indicatorAnimator?.removeAllListeners(),
indicatorAnimator?.removeAllUpdateListeners() and set indicatorAnimator = null
(also clear any animation on the indicator view if present), and update
startBlinkingIndicator() to assign the created animator to this property so it
can be cleaned up when the view is destroyed.

In
`@app/src/main/java/com/itsaky/androidide/utils/ui/TemplateScrollGateKeeper.kt`:
- Around line 59-63: The detach() method calls
recyclerView.viewTreeObserver.removeOnGlobalLayoutListener(globalLayoutListener)
unconditionally which can throw if the ViewTreeObserver is dead; before removing
the listener, guard with recyclerView.viewTreeObserver.isAlive (or obtain the
observer into a local variable and check its isAlive) and only call
removeOnGlobalLayoutListener(globalLayoutListener) when true, leaving the other
cleanup (removeOnScrollListener, removeOnLayoutChangeListener,
onScrollStateChanged = null) unchanged; update TemplateScrollGateKeeper.detach()
to perform this check around the globalLayoutListener removal.

In `@app/src/main/res/layout/fragment_template_details.xml`:
- Around line 70-79: The ImageView with id "scrollIndicator" is decorative and
should be removed from accessibility focus; update the <ImageView> in
fragment_template_details.xml (id scrollIndicator) to mark it decorative by
adding android:contentDescription="`@null`" and
android:importantForAccessibility="no" (and optionally
android:focusable="false") so screen readers ignore the non-actionable guidance
icon.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ec301568-d340-4d1e-ae81-530f23189dc4

📥 Commits

Reviewing files that changed from the base of the PR and between 57faa9a and a6f8cfd.

📒 Files selected for processing (4)
  • app/src/main/java/com/itsaky/androidide/fragments/TemplateDetailsFragment.kt
  • app/src/main/java/com/itsaky/androidide/utils/ProjectCreationManager.kt
  • app/src/main/java/com/itsaky/androidide/utils/ui/TemplateScrollGateKeeper.kt
  • app/src/main/res/layout/fragment_template_details.xml

Comment thread app/src/main/res/layout/fragment_template_details.xml
@jatezzz jatezzz force-pushed the fix/ADFA-3718-require-scroll-project-creation branch from a6f8cfd to 80302c3 Compare May 20, 2026 13:44
@jatezzz jatezzz requested review from a team, Daniel-ADFA and dara-abijo-adfa May 20, 2026 13:52
@hal-eisen-adfa
Copy link
Copy Markdown
Collaborator

Might want to add some TalkBack for visual impaired users.

@jatezzz jatezzz force-pushed the fix/ADFA-3718-require-scroll-project-creation branch from 7c4683d to 3713bc3 Compare May 20, 2026 18:29
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
app/src/main/java/com/itsaky/androidide/fragments/TemplateDetailsFragment.kt (1)

194-203: 💤 Low value

Use the existing canFinish variable instead of recalculating.

Line 200 duplicates the expression from line 197. Use canFinish for consistency.

♻️ Suggested fix
     private fun updateFinishEnabledState() {
         val isCreating = viewModel.creatingProject.value ?: false
         val hasScrolledToBottom = scrollGateKeeper?.hasReachedEnd ?: false
         val canFinish = !isCreating && hasScrolledToBottom
         val stateDesc = if (canFinish) null else getString(string.msg_scroll_to_create_project)

-        binding.finish.isEnabled = !isCreating && hasScrolledToBottom
+        binding.finish.isEnabled = canFinish
         binding.scrollIndicator.isVisible = !hasScrolledToBottom
         ViewCompat.setStateDescription(binding.finish, stateDesc)
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/com/itsaky/androidide/fragments/TemplateDetailsFragment.kt`
around lines 194 - 203, The method updateFinishEnabledState() recalculates the
same boolean twice; use the already computed canFinish instead of re-evaluating
!isCreating && hasScrolledToBottom. Replace the expression assigned to
binding.finish.isEnabled with canFinish, keep binding.scrollIndicator.isVisible
= !hasScrolledToBottom, and continue calling
ViewCompat.setStateDescription(binding.finish, stateDesc) so the logic uses the
single source of truth (canFinish) for the finish button enabled state.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In
`@app/src/main/java/com/itsaky/androidide/fragments/TemplateDetailsFragment.kt`:
- Around line 194-203: The method updateFinishEnabledState() recalculates the
same boolean twice; use the already computed canFinish instead of re-evaluating
!isCreating && hasScrolledToBottom. Replace the expression assigned to
binding.finish.isEnabled with canFinish, keep binding.scrollIndicator.isVisible
= !hasScrolledToBottom, and continue calling
ViewCompat.setStateDescription(binding.finish, stateDesc) so the logic uses the
single source of truth (canFinish) for the finish button enabled state.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a9fc8886-4808-45e3-8bb3-cbc866669caf

📥 Commits

Reviewing files that changed from the base of the PR and between 80302c3 and 3713bc3.

📒 Files selected for processing (5)
  • app/src/main/java/com/itsaky/androidide/fragments/TemplateDetailsFragment.kt
  • app/src/main/java/com/itsaky/androidide/utils/ProjectCreationManager.kt
  • app/src/main/java/com/itsaky/androidide/utils/ui/TemplateScrollGateKeeper.kt
  • app/src/main/res/layout/fragment_template_details.xml
  • resources/src/main/res/values/strings.xml
✅ Files skipped from review due to trivial changes (1)
  • resources/src/main/res/values/strings.xml

@jatezzz jatezzz requested a review from Daniel-ADFA May 20, 2026 20:24
jatezzz added 3 commits May 20, 2026 16:59
Adds scroll detection to ensure users reach the end of the project setup before continuing.
…the `blinkAnimator` object and accessibility
@jatezzz jatezzz force-pushed the fix/ADFA-3718-require-scroll-project-creation branch from d53c44d to 2fbf11f Compare May 20, 2026 21:59
@jatezzz jatezzz merged commit ffba855 into stage May 21, 2026
2 checks passed
@jatezzz jatezzz deleted the fix/ADFA-3718-require-scroll-project-creation branch May 21, 2026 13:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants